package com.slipper.core.file.minio;

import com.slipper.core.file.IFileService;
import com.slipper.core.file.vo.FileVO;
import com.slipper.util.Tool;
import io.minio.*;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.nio.file.Files;

public class MinioFileService implements IFileService {
    private static final String MODE = "minio";
    private final String endpoint;
    private final String accessKey;
    private final String secretKey;
    private final String bucketName;
    private final MinioClient minioClient;

    public MinioFileService(String endpoint, String accessKey, String secretKey, String bucketName) throws Exception {
        this.endpoint = endpoint;
        this.accessKey = accessKey;
        this.secretKey = secretKey;
        this.bucketName = bucketName;
        this.minioClient = createClient();
    }

    public MinioClient createClient() throws Exception {
        MinioClient minioClient = MinioClient.builder()
                .endpoint(this.endpoint)
                .credentials(this.accessKey, this.secretKey)
                .build();

        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(this.bucketName).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(this.bucketName).build());
        }

        return minioClient;
    }

    @Override
    public FileVO saveFile(String fileName, byte[] bytes) throws Exception {
        String objectName = getFileName() + getExtension(fileName);
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(this.bucketName)
                        .object(objectName)
                        .stream(new ByteArrayInputStream(bytes), bytes.length, -1)
                        .build());

        FileVO fileVO = new FileVO();
        fileVO.setMode(MODE);
        fileVO.setName(Tool.isBlank(fileName) ? getFileName() : fileName);
        fileVO.setPath(objectName);
        fileVO.setSize((long) bytes.length);
        return fileVO;
    }

    @Override
    public FileVO saveFile(File file) throws Exception {
        String objectName = getFileName() + getExtension(file.getName());
        byte[] bytes = Files.readAllBytes(file.toPath());
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(this.bucketName)
                        .object(objectName)
                        .stream(new ByteArrayInputStream(bytes), bytes.length, -1)
                        .build());

        FileVO fileVO = new FileVO();
        fileVO.setMode(MODE);
        fileVO.setName(file.getName());
        fileVO.setPath(objectName);
        fileVO.setSize(file.getUsableSpace());
        return fileVO;
    }

    @Override
    public byte[] readFile(String objectName) throws Exception {
        GetObjectResponse getObjectResponse = minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(this.bucketName)
                        .object(objectName)
                        .build());
        return getObjectResponse.readAllBytes();
    }

    @Override
    public void readFile(String objectName, String rangeHeader, HttpServletResponse response) throws Exception {
        StatObjectResponse statObjectResponse = minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());

        long fileSize = statObjectResponse.size();
        long startPos = 0;
        long endPos = fileSize - 1;
        if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            String numRange = rangeHeader.replaceAll("bytes=", "");
            if (numRange.startsWith("-")) {
                endPos = fileSize - Long.parseLong(numRange.substring(1));
            } else if (numRange.endsWith("-")) {
                startPos = Long.parseLong(numRange.substring(0, numRange.length() - 1));
                endPos = fileSize - 1;
            } else {
                String[] strRange = numRange.split("-");
                if (strRange.length == 2) {
                    startPos = Long.parseLong(strRange[0].trim());
                    endPos = Long.parseLong(strRange[1].trim());
                }
            }

            if (startPos < 0 || endPos < 0 || endPos >= fileSize || startPos > endPos) {
                response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                return;
            }
        }

        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Content-Type", "video/mov");
        response.setHeader("Content-Range", String.format("bytes %d-%d/%d", startPos, endPos, fileSize));
        response.setHeader("Content-Length", String.valueOf(endPos - startPos + 1));

        InputStream inputStream = minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .offset(startPos)
                        .length(endPos - startPos + 1)
                        .build());

        OutputStream outputStream = response.getOutputStream();
        IOUtils.copy(inputStream, outputStream);
        outputStream.flush();
    }

    @Override
    public void saveFileToPath(String objectName, byte[] bytes) throws Exception {
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(this.bucketName)
                        .object(objectName)
                        .stream(new ByteArrayInputStream(bytes), bytes.length, -1)
                        .build());
    }
}
